package com._119yn.host.gst200.cansocket.handler;

import com._119yn.host.gst200.cansocket.message.CanFrame;
import com._119yn.host.gst200.service.CanFrameSender;
import com._119yn.host.gst200.service.DtuService;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import lombok.Synchronized;
import lombok.extern.slf4j.Slf4j;

import java.util.Collection;

@Slf4j
public class CanFrameHandler extends ChannelHandlerAdapter implements DefaultChannelInboundHandler, DefaultChannelOutboundHandler, CanFrameSender {

    /**
     * 主机服务
     */
    private final DtuService dtuService;

    /**
     * netty通信上下文
     */
    private ChannelHandlerContext ctx;

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        super.exceptionCaught(ctx, cause);
    }

    /**
     * 构造函数
     *
     * @param dtuService 主机服务
     */
    public CanFrameHandler(DtuService dtuService) {
        this.dtuService = dtuService;
        this.dtuService.register(this);
    }

    /**
     * 通道注册完成
     *
     * @param ctx netty通信上下文
     * @throws Exception 异常信息
     */
    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        this.ctx = ctx;
        this.ctx.fireChannelRegistered();
    }

    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        this.ctx.fireChannelUnregistered();
        this.ctx = null;
    }

    /**
     * 数据抵达
     *
     * @param ctx netty通信上下文
     * @param msg 消息
     * @throws Exception 异常
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (msg instanceof CanFrame frame) {
            dtuService.onCanDataReceived(frame);
        }
        ctx.fireChannelRead(msg);
    }

    private boolean isChannelActive() {
        return ctx != null && ctx.channel() != null && ctx.channel().isActive();
    }

    @Synchronized
    @Override
    public void send(CanFrame frame) {
        if (isChannelActive()) {
            ctx.writeAndFlush(frame);
        }
    }

    @Synchronized
    @Override
    public void send(Collection<CanFrame> frames) {
        if (isChannelActive()) {
            for (CanFrame frame : frames) {
                ctx.write(frame);
            }
            ctx.flush();
        }
    }
}
